ASN.1 Компілятор ASN1SCG

У статті представлений мінімальний Swift "ASN.1 компілятор" на мові Erlang.
Автор — Максим Сохацький, 2023-8-8.

Зміст

1) Контест ASN.1 компіляторів
2) X.509 DER vs Erlang BERT/ETF
3) Бібліотека Apple
4) Erlang AST ASN.1 Swift кодогенаратора

Вступ

Питання вибору серіалізатора залежить від типу інфраструктури в компанії. Якшо ми говоримо про гомогенні інтерфейси та бібліотеки прикладного програмування то зазвичай вони достатньо розвинені аби автоматично генерувати структури для основних мов програмування для яких анонсується серіалізатор. Хоча формально йдеться про AST трансформацію з мови визначення типів структур в цільову мову програмування, такі системи називаються компіляторами мови визначення даних.

У світі існує багато рантаймів, і кожен з них пропонує свій рідний серіалізатор, який в незмінному вигляді представляє дані які циркулюють в рантаймі або віртуальній машині. Так на мовах .NET, Java, Haskell, Erlang є такі природні серіалазотори. В гомогенних архітектурах, де все побудовано навколо однії мови програмування, прийнято використовувати цей серіалізатор як основний для всіх сервісів написаних навіть на різних мовах програмування, так як BERT-RPC протокол який використовувався в Github.

Окремо виділяються формати які не прив'язані до однієї мови, а пропонуються як універсальні серіалізатори, такі як IDL COM/DCOM, ASN.1 DER, Erlang BERT/ETF, GRPC Gproto, SOAP XML/WBXML, та інші бінарні мови і серіалізатори.

Всі сертифікати в браузері, SSH ключі, PGP/GPG ключі, закриті і відкриті конверти, криптографічні повідомлення CMS, протоколи видачі сертифікатів, LDAP директорія та ще багато іншого включачи GSM/LTE та майже всі телекомунікаційні повідомлення визначаються за допомогою ASN.1.

Компілятори

Я хотів зробити огляд ASN.1 компіляторів, але з представлених безкоштовних жоден крім Erlang-ового не компілює повний набір ASN.1 файлів проекта SYNRC CA. Ні C-шний кацапський який використовується в Apple ASN1C, Ні F#-повий ASN1SCC. Залишається сподіватися, що генератор С-шного коду Фабріса Белара ASN1CC зможе це зробити однак з огляду на його клієнтів навряд чи ми про це дізнаємося, так шоб прямо розказати в блозі.

Я подивився на помилки представлених на сайті ITU компіляторів і складається враження що сумісність з самим ж файлами дефініціями ITU ніхто не перевіряє навіть мануально. Просто вивішують в залікову таблицю будь-кого хто успішно розпарсав сабсет ASN.1 і згенерував якийсь граничні імплементація для свого випадку.

Тестовий набір файлів

Ось текстовий сет з сайту ITU яким я перевіряв компілятори.

AESKeyWrapWithPad-02.asn1 AESKeyWrapWithPad-88.asn1 ANSI-X9-42.asn1 ANSI-X9-62.asn1 AlgorithmInformation-2009.asn1 AttributeCertificateVersion1-2009.asn1 AuthenticationFramework.asn1 BasicAccessControl.asn1 CHAT.asn1 CMS-AES-CCM-and-AES-GCM-2009.asn1 CMSAesRsaesOaep-2009.asn1 CMSECCAlgs-2009-02.asn1 CMSECDHAlgs-2017.asn1 CertificateExtensions.asn1 Character-Coding-Attributes.asn1 Character-Presentation-Attributes.asn1 Character-Profile-Attributes.asn1 Colour-Attributes.asn1 CryptographicMessageSyntax-2009.asn1 CryptographicMessageSyntax-2010.asn1 CryptographicMessageSyntaxAlgorithms-2009.asn1 DOR-definition.asn1 Default-Value-Lists.asn1 DirectoryAbstractService.asn1 Document-Profile-Descriptor.asn1 EnrollmentMessageSyntax-2009.asn1 ExtendedSecurityServices-2009.asn1 External-References.asn1 Geo-Gr-Coding-Attributes.asn1 Geo-Gr-Presentation-Attributes.asn1 Geo-Gr-Profile-Attributes.asn1 ISO-STANDARD-9541-FONT-ATTRIBUTE-SET.asn1 ISO9541-SN.asn1 Identifiers-and-Expressions.asn1 InformationFramework.asn1 KEP.asn1 LDAP.asn1 Layout-Descriptors.asn1 Link-Descriptors.asn1 Location-Expressions.asn1 Logical-Descriptors.asn1 MultipleSignatures-2010.asn1 OCSP.asn1 PKCS-10.asn1 PKCS-12.asn1 PKCS-5.asn1 PKCS-7.asn1 PKCS-8.asn1 PKCS-9.asn1 PKIX-CommonTypes-2009.asn1 PKIX-X400Address-2009.asn1 PKIX1-PSS-OAEP-Algorithms-2009.asn1 PKIX1Explicit-2009.asn1 PKIX1Explicit88.asn1 PKIX1Implicit-2009.asn1 PKIX1Implicit88.asn1 PKIXAlgs-2009.asn1 PKIXAttributeCertificate-2009.asn1 PKIXCMP-2009.asn1 PKIXCRMF-2009.asn1 Raster-Gr-Coding-Attributes.asn1 Raster-Gr-Presentation-Attributes.asn1 Raster-Gr-Profile-Attributes.asn1 SMIMESymmetricKeyDistribution-2009.asn1 SecureMimeMessageV3dot1-2009.asn1 SelectedAttributeTypes.asn1 Style-Descriptors.asn1 Subprofiles.asn1 Temporal-Relationships.asn1 Text-Units.asn1 UpperBounds.asn1 UsefulDefinitions.asn1 Videotex-Coding-Attributes.asn1

Фірмові і стандартні

Тут розглядаються фірмова бібліотека RPC та стандартна ASN1X компанії SYNRC для бінарної серіалізації внутрішнього бінарного представлення віртуальної машини Erlang та бінарного представлення сімейства розміточних форматів і їх парсерів BER, DER, PER, кодери і декодери яких генеруються з мови специфікацій та протоколів ASN.1.

Ericsson Erlang BERT/ETF

Серед фірмових можна відзначити Ericsson Binary Erlang Term Format, який використовується в промисловості більше 10 років починаючи з Github. SYNRC використовує бібліотеку SYNRC RPC яка генерує кодери і декодери (API SDK) для будь яких Erlang структур на наступні мови програмування та мови визначення протоколів:

1) IDL/COM;
2) JavaScript;
3) Google gproto;
4) Apple Swift;
5) Erlang validation.

Завдяки генератору BERT парсерів SYNRC RPC компанія NYNJA змогла уніфіковано працювати з об'єктами системи в двох форматах BERT і GPROTO і залишилася після апробації на BERT.

ITU IETF ASN.1 BER/DER/PER Apple/Microsoft/OpenSSL

До стандартних можна віднести бінарні формати для серіалізації сертифікатів та обгорток криптографічних ключів які використовуються в PKI X.509. Завдяки промисловому і повному компілятору ASN.1 у складі Erlang/OTP ми маємо змогу побудувати повністю API SDK інфраструктуру на Erlang мінімальними зусиллями не гублячи сумісність з фірмовим форматом BERT/ETF, так як він все одно використовується в середині віртуальної машини після конвертації. Так була продемонстрована сумісність протоколів CHAT BERT (HRL) та результату ASN.1 компіляції CHAT BER.

1) ASN1C;
2) ASN1CC;
3) ASN1SCC;
4) ASN1SCG (Swift Code Generation);
5) ASN1JCG (Java Code Generation).

В даній статті розказується про один з двох генераторів коду DER/BER/PER кодерів і декодерів SYNRC для мови Swift — ASN1SCG, який генерує код невідмінний від схеми кодування і декодування яку Apple пропонує в свої бібліотеках asn1, crypto та certificates.

Google gproto/GRPC

В першу чергу цей формат набув популярності на пристроях Apple, так як протокол основного AP сховища Apple Cassandra використовує цей формат. Значною проблему є переускладеність кодогенератора і інфраструктури компілятора.

Бібліотеки Apple

Починаючи з вести 2020 року, коли Apple анонсувала CryptoKit для X.509 інфраструктури зокрема, компанія випустила наступні бібліотеки, які показується стандартний механізм який Apple передбачає для Swift авторів як вони мусять користуватися PKI X.509 обгортками, сертифікатами, ключами та взагалі будь-якими ASN.1 протоколами.

1) apple/swift-asn1
2) apple/swift-crypto
3) apple/swift-certificates

SYNRC ASN1SCG — це ASN.1 компілятор в мову Swift з використанням схеми кодування Apple, написаний на мові Erlang компанією SYNRC. Тут показуються приклади генерації кодерів і декодерів для деяких структур сімейства PKI X.509 в форматі прикладів Apple (насправді відрізнити конкретно ці неможливо).

ECDSASignature

ASN.1 визначення структури ECDSASignature.

ECDSASignature ::= SEQUENCE { r INTEGER, s INTEGER }

Кодер

func serialize(into coder: inout DER.Serializer, withIdentifier identifier: ASN1Identifier) throws { try coder.appendConstructedNode(identifier: identifier) { coder in try coder.serialize(self.r) try coder.serialize(self.s) } }

Декодер

init(derEncoded root: ASN1Node, withIdentifier identifier: ASN1Identifier) throws { self = try DER.sequence(rootNode, identifier: identifier) { nodes in let r = try ArraySlice(derEncoded: &nodes) let s = try ArraySlice(derEncoded: &nodes) return ECDSASignature(r: r, s: s) } }

SubjectPublicKeyInfo

ASN.1 визначення структури SubjectPublicKeyInfo.

SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier, subjectPublicKey BIT STRING }

Кодер

func serialize(into coder: inout DER.Serializer, withIdentifier identifier: ASN1Identifier) throws { try coder.appendConstructedNode(identifier: identifier) { coder in try coder.serialize(self.algorithmIdentifier) try coder.serialize(self.key) } }

Декодер

init(derEncoded root: ASN1Node, withIdentifier identifier: ASN1Identifier) throws { self = try DER.sequence(root, identifier: identifier) { nodes in let algid = try AlgorithmIdentifier(derEncoded: &nodes) let key = try ASN1BitString(derEncoded: &nodes) return SubjectPublicKeyInfo(algorithmIdentifier: algid, key: key) } }

TBSCertificate

ASN.1 визначення структури TBSCertificate.

TBSCertificate ::= SEQUENCE { version [0] Version DEFAULT v1, serialNumber CertificateSerialNumber, signature AlgorithmIdentifier, issuer Name, validity Validity, subject Name, subjectPublicKeyInfo SubjectPublicKeyInfo, issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, extensions [3] Extensions OPTIONAL } Version ::= INTEGER { v1(0), v2(1), v3(2) } CertificateSerialNumber ::= INTEGER UniqueIdentifier ::= BIT STRING Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension

Кодер

init(derEncoded root: ASN1Node, withIdentifier identifier: ASN1Identifier) throws { self = try DER.sequence(rootNode, identifier: identifier) { nodes in let version = try DER.decodeDefaultExplicitlyTagged(&nodes, tagNumber: 0, tagClass: .contextSpecific, defaultValue: Int(0)) guard (0...2).contains(version) else { throw ASN1Error.invalidASN1Object( reason: "Invalid X.509 version \(version)") } let serialNumber = try ArraySlice(derEncoded: &nodes) let signature = try AlgorithmIdentifier(derEncoded: &nodes) let issuer = try DistinguishedName(derEncoded: &nodes) let validity = try Validity(derEncoded: &nodes) let subject = try DistinguishedName(derEncoded: &nodes) let subjectPublicKeyInfo = try SubjectPublicKeyInfo(derEncoded: &nodes) let issuerUniqueID = try DER.optionalExplicitlyTagged(&nodes, tagNumber: 1, tagClass: .contextSpecific) { try UniqueIdentifier(derEncoded: $0) } let subjectUniqueID = try DER.optionalExplicitlyTagged(&nodes, tagNumber: 2, tagClass: .contextSpecific) { try UniqueIdentifier(derEncoded: $0) } let extensions = try DER.optionalExplicitlyTagged(&nodes, tagNumber: 3, tagClass: .contextSpecific) { try DER.sequence(of: Certificate.Extension.self, identifier: .sequence, rootNode: $0) } return TBSCertificate( version: Certificate.Version(rawValue: version), serialNumber: Certificate.SerialNumber( bytes: serialNumber), signature: Certificate.SignatureAlgorithm( algorithmIdentifier: signature), issuer: issuer, validity: validity, subject: subject, publicKey: try Certificate.PublicKey( spki: subjectPublicKeyInfo), issuerUniqueID: issuerUniqueID, subjectUniqueID: subjectUniqueID, extensions: try Certificate.Extensions(extensions ?? []) ) } }

Декодер

func serialize(into coder: inout DER.Serializer, withIdentifier identifier: ASN1Identifier) throws { try coder.appendConstructedNode(identifier: identifier) { coder in if self.version != .v1 { try coder.serialize(self.version.rawValue, explicitlyTaggedWithTagNumber: 0, tagClass: .contextSpecific) } try coder.serialize(self.serialNumber.bytes) try coder.serialize(AlgorithmIdentifier(self.signature)) try coder.serialize(self.issuer) try coder.serialize(self.validity) try coder.serialize(self.subject) try coder.serialize(SubjectPublicKeyInfo(self.publicKey)) if let issuerUniqueID = self.issuerUniqueID { try coder.serialize(issuerUniqueID, explicitlyTaggedWithTagNumber: 1, tagClass: .contextSpecific) } if let subjectUniqueID = self.subjectUniqueID { try coder.serialize(subjectUniqueID, explicitlyTaggedWithTagNumber: 2, tagClass: .contextSpecific) } if self.extensions.count > 0 { try coder.serialize(explicitlyTaggedWithTagNumber: 3, tagClass: .contextSpecific) { coder in try coder.serializeSequenceOf(extensions) } } } }

Кодогенерація на Erlang в Swift

[x] Sequence [x] Choice [x] Enumeration [x] Integer enumeration [x] Sequence Of [x] Any [x] Null [x] Integer [x] Strings

Висновки

Я взяв наймолодші вендор бібліотеки Apple для схем кодування і декодування ASN.1 форматів BER/DER і існуючу структуру ASN.1 компілятора Ericsson, єдиного вільного повного, і написав генератор перших (схем кодування) використовуючи типову інформацію другого (ASN.1 компілятора Erlang/OTP).

Щодо ефективності, то всі зауваження щодо неї можна направляти напряму Apple, так як наш компілятор намагається просто бути максимально сумісним і робити вигляд шо цей код був написаний співробітниками Apple.

Якщо ж досліджувати моделі бездоганної або гранично-ефективної компіляції, то вона може бути досягнена виключно генерацією ассемблерних інструкцій і по можливості векторних і паралельних схем парсінгу з представленнями доведеннями ефективності через аналіз заповнення конвеєрів процесора і підрахунку циклів процесора на операції.


˙


˙

[1]. ITU-T ASN.1 Compilers
[2]. Apple Swift ASN.1 Library Documentation.
[3]. Let's Encrypt. Ласкаво просимо в ASN.1 і DER
[4]. Swift Crypto Library Documentation
[5]. Swift Certificates Library Documentation
[6]. Olivier Dubuisson. ASN.1 Communication between Heterogeneous Systems